/* tolua: functions to push C values.
** Support code for Lua bindings.
** Written by Waldemar Celes
** TeCGraf/PUC-Rio
** Apr 2003
** $Id: $
*/

/* This code is free software; you can redistribute it and/or modify it.
** The software provided hereunder is on an "as is" basis, and
** the author has no obligation to provide maintenance, support, updates,
** enhancements, or modifications.
*/

#include "tolua++.h"
#include "lauxlib.h"

#include <stdlib.h>

TOLUA_API void tolua_pushvalue(lua_State* L, int lo) {
    lua_pushvalue(L, lo);
}

TOLUA_API void tolua_pushboolean(lua_State* L, int value) {
    lua_pushboolean(L, value);
}

TOLUA_API void tolua_pushnumber(lua_State* L, lua_Number value) {
    lua_pushnumber(L, value);
}

TOLUA_API void tolua_pushinteger(lua_State* L, lua_Integer n) {
    lua_pushinteger(L, n);
}
TOLUA_API void tolua_pushstring(lua_State* L, const char* value) {
    if (value == NULL) {
        lua_pushnil(L);
    }
    else {
        lua_pushstring(L, value);
    }
}

TOLUA_API void tolua_pushlstring(lua_State* L, const char* value, size_t len) {
    if (value == NULL) {
        lua_pushnil(L);
    }
    else {
        lua_pushlstring(L, value, len);
    }
}

TOLUA_API void tolua_pushuserdata(lua_State* L, void* value) {
    if (value == NULL) {
        lua_pushnil(L);
    }
    else {
        lua_pushlightuserdata(L, value);
    }
}

TOLUA_API void tolua_pushusertype(lua_State* L, void* value,
    const char* type) {
    if (value == NULL) {
        lua_pushnil(L);
    }
    else {
        luaL_getmetatable(L, type);
        lua_pushstring(L, "tolua_ubox");
        lua_rawget(L, -2);     /* stack: mt ubox */

        if (lua_isnil(L, -1)) {
            lua_pop(L, 1);
            lua_pushstring(L, "tolua_ubox");
            lua_rawget(L, LUA_REGISTRYINDEX);
        };

        lua_pushlightuserdata(L, value);

        lua_rawget(L, -2);                    /* stack: mt ubox ubox[u] */

        if (lua_isnil(L, -1)) {
            lua_pop(L, 1);                       /* stack: mt ubox */
            lua_pushlightuserdata(L, value);
            *(void**)lua_newuserdata(L,
                sizeof(void*)) = value; /* stack: mt ubox u newud */
            lua_pushvalue(L, -1);                /* stack: mt ubox u newud newud */
            lua_insert(L, -4);                   /* stack: mt newud ubox u newud */
            lua_rawset(L, -3);                   /* stack: mt newud ubox */
            lua_pop(L, 1);                       /* stack: mt newud */
            /*luaL_getmetatable(L,type);*/
            lua_pushvalue(L, -2);          /* stack: mt newud mt */
            lua_setmetatable(L, -2);       /* stack: mt newud */
#ifdef LUA_VERSION_NUM
            lua_pushvalue(L, TOLUA_NOPEER);
#if LUA_VERSION_NUM > 501
            lua_setuservalue(L, -2);
#else
            lua_setfenv(L, -2);
#endif
#endif
        }
        else {
            /* check the need of updating the metatable to a more specialized class */
            lua_insert(L, -2);                    /* stack: mt ubox[u] ubox */
            lua_pop(L, 1);                        /* stack: mt ubox[u] */
            lua_pushstring(L, "tolua_super");
            lua_rawget(L, LUA_REGISTRYINDEX);     /* stack: mt ubox[u] super */
            lua_getmetatable(L, -2);              /* stack: mt ubox[u] super mt */
            lua_rawget(L, -2);                    /* stack: mt ubox[u] super super[mt] */

            if (lua_istable(L, -1)) {
                lua_pushstring(L,
                    type);              /* stack: mt ubox[u] super super[mt] type */
                lua_rawget(L,
                    -2);                    /* stack: mt ubox[u] super super[mt] flag */

                if (lua_toboolean(L, -1) == 1) { /* if true */
                    lua_pop(L, 3); /* mt ubox[u]*/
                    lua_remove(L, -2);
                    return;
                }
            }

            /* type represents a more specilized type */
            /*luaL_getmetatable(L,type);             // stack: mt ubox[u] super super[mt] flag mt */
            lua_pushvalue(L,
                -5);                 /* stack: mt ubox[u] super super[mt] flag mt */
            lua_setmetatable(L,
                -5);             /* stack: mt ubox[u] super super[mt] flag */
            lua_pop(L, 3);                       /* stack: mt ubox[u] */
        }

        lua_remove(L, -2);  /* stack: ubox[u]*/
    }
}

TOLUA_API void tolua_pushusertype_and_takeownership(lua_State* L, void* value,
    const char* type) {
    tolua_pushusertype(L, value, type);
    tolua_register_gc(L, lua_gettop(L));
}

TOLUA_API void tolua_pushfieldvalue(lua_State* L, int lo, int index, int v) {
    lua_pushnumber(L, index);
    lua_pushvalue(L, v);
    lua_settable(L, lo);
}

TOLUA_API void tolua_pushfieldboolean(lua_State* L, int lo, int index,
    int v) {
    lua_pushnumber(L, index);
    lua_pushboolean(L, v);
    lua_settable(L, lo);
}


TOLUA_API void tolua_pushfieldnumber(lua_State* L, int lo, int index,
    lua_Number v) {
    lua_pushnumber(L, index);
    tolua_pushnumber(L, v);
    lua_settable(L, lo);
}

TOLUA_API void tolua_pushfieldstring(lua_State* L, int lo, int index,
    const char* v) {
    lua_pushnumber(L, index);
    tolua_pushstring(L, v);
    lua_settable(L, lo);
}

TOLUA_API void tolua_pushfielduserdata(lua_State* L, int lo, int index,
    void* v) {
    lua_pushnumber(L, index);
    tolua_pushuserdata(L, v);
    lua_settable(L, lo);
}

TOLUA_API void tolua_pushfieldusertype(lua_State* L, int lo, int index,
    void* v, const char* type) {
    lua_pushnumber(L, index);
    tolua_pushusertype(L, v, type);
    lua_settable(L, lo);
}

TOLUA_API void tolua_pushfieldusertype_and_takeownership(lua_State* L, int lo,
    int index, void* v, const char* type) {
    lua_pushnumber(L, index);
    tolua_pushusertype(L, v, type);
    tolua_register_gc(L, lua_gettop(L));
    lua_settable(L, lo);
}
